Skip to content

feat: adds long lived refresh token feature for ci#475

Open
v3nant wants to merge 4 commits intomainfrom
feat/long-token-provision
Open

feat: adds long lived refresh token feature for ci#475
v3nant wants to merge 4 commits intomainfrom
feat/long-token-provision

Conversation

@v3nant
Copy link
Contributor

@v3nant v3nant commented Feb 12, 2026

CI Login Token Exchange: Long-Lived Refresh Tokens for Headless Auth

Summary

Adds support for long-lived organization-scoped refresh tokens for headless CI/CD pipelines. Users can provision a token once (interactive) and use it in CI without running hd auth login in the pipeline.

Updates (Provision orgId from User Setup): Provision no longer requires --org-id; orgId is obtained from the user setup API. The hd auth-ci login command has been removed—scans exchange the CI token for an access token automatically via requireAccessTokenForScan.

Design

Flow

sequenceDiagram
    participant User
    participant Login
    participant Provision
    participant UserSetup
    participant Scan

    User->>Login: hd auth login
    Login->>Login: OAuth PKCE, persist tokens (keyring)

    User->>Provision: hd auth-ci provision
    Provision->>UserSetup: getUserSetupStatus / completeUserSetup
    UserSetup-->>Provision: orgId
    Provision->>IAM: getOrgAccessTokens(orgId, null) [Bearer from login]
    IAM-->>Provision: refreshToken (long-lived)
    Provision->>Provision: saveCIToken, saveCIOrgId

    Note over User: CI pipeline runs (headless)
    User->>Scan: hd scan eol (HD_AUTH_TOKEN, HD_ORG_ID set)
    Scan->>Scan: requireAccessTokenForScan → requireCIAccessToken
    Scan->>IAM: getOrgAccessTokens(orgId, refreshToken)
    IAM-->>Scan: accessToken, refreshToken (maybe rotated)
    Scan->>Scan: uses accessToken for API calls
Loading

Auth Resolution Order

requireAccessTokenForScan (used by scan, Apollo default, completeUserSetup) now:

  1. CI first: If getCIToken() or config.accessTokenFromEnv → use CI path (requireCIAccessToken)
  2. Keyring second: Else use stored OAuth tokens (keyring) with refresh via Keycloak

CI tokens come from:

  • Env (CI): HD_AUTH_TOKEN, HD_ORG_ID, HD_ACCESS_TOKEN
  • File (local): ~/.hdcli/ci-token (encrypted, machine-bound)

Prerequisites

  • eol-api PR feat: analytics #301 must be merged before deploying these CLI changes. The new UserSetupStatus schema ({ isComplete, orgId }) is required.

Changes

Commands

Command Purpose
hd auth-ci provision Provision long-lived CI token (requires hd auth login first). Obtains orgId from user setup API. Saves to ~/.hdcli/ci-token and outputs token for CI secrets.

Removed: hd auth-ci login — redundant. Scans call requireAccessTokenForScanrequireCIAccessToken, which exchanges the CI token internally.

New / Updated Files

  • src/commands/auth-ci/provision.ts – CI provision (no --org-id; uses ensureUserSetup for orgId)
  • src/api/user-setup.client.tsgetUserSetupStatus and completeUserSetup return { isComplete, orgId }; ensureUserSetup returns orgId
  • src/api/gql-operations.tsuserSetupStatusQuery and completeUserSetupMutation use UserSetupStatus object schema
  • src/service/ci-auth.svc.ts – CI auth path; error messages updated (no --org-id references)
  • src/service/ci-token.svc.ts – CI token storage (env + encrypted file via conf); saveCIOrgId / getCIOrgId for org persistence
  • src/api/ci-token.client.ts – IAM getOrgAccessTokens client (provisionCIToken, exchangeCITokenForAccess)
  • src/api/apollo.client.ts – Extracted from nes.client.ts; shared Apollo factory with token provider
  • src/config/constants.ts – Added ENABLE_AUTH, ENABLE_USER_SETUP, ciTokenFromEnv, orgIdFromEnv, accessTokenFromEnv, IAM config
  • src/service/auth.svc.tsrequireAccessTokenForScan delegates to CI path first, re-exports CITokenError

README

  • CI/CD authentication section updated with new flow
  • One-time setup: hd auth loginhd auth-ci provision (no --org-id)
  • CI pipeline: hd scan eol directly with HD_AUTH_TOKEN and HD_ORG_ID (no eval step)
  • GitHub Actions and GitLab CI examples updated

Other

  • nes.client.ts: Uses shared createApollo from apollo.client.ts
  • user-setup.client.ts: completeUserSetup uses requireAccessTokenForScan; ensureUserSetup extended to return orgId for provision

Usage

One-time setup (interactive):

hd auth login
hd auth-ci provision

Copy token output into CI secrets: HD_AUTH_TOKEN, HD_ORG_ID (orgId also stored at provision time when using local file).

CI pipeline:

export HD_ORG_ID=<id> HD_AUTH_TOKEN="<token>"
hd scan eol --dir .

Gaps & Follow-ups

Gap Notes
Docker image auth README Docker examples do not show CI auth; document if ghcr.io/herodevs/eol-scan supports HD_AUTH_TOKEN / HD_ORG_ID.
E2E with real auth E2E no longer mocks auth; verify E2E still passes when ENABLE_AUTH is false or with CI token injection.

@v3nant v3nant force-pushed the feat/long-token-provision branch 8 times, most recently from 6b414ed to da30ac7 Compare February 13, 2026 15:56
@v3nant v3nant marked this pull request as ready for review February 13, 2026 17:07
@v3nant v3nant requested a review from a team as a code owner February 13, 2026 17:07
}
}

let response = await fetch(input, { ...init, headers });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you dont need the body consider performing a HEAD request. This improves the memory footprint
https://undici.nodejs.org/#/?id=garbage-collection

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the body for the graphql queries and mutations, as we need Apollo to parse the GraphQL data and the request.

@herodevs herodevs deleted a comment from chatgpt-codex-connector bot Feb 15, 2026
chatgpt-codex-connector[bot]

This comment was marked as resolved.

@herodevs herodevs deleted a comment from chatgpt-codex-connector bot Feb 15, 2026
@herodevs herodevs deleted a comment from chatgpt-codex-connector bot Feb 15, 2026
@v3nant v3nant force-pushed the feat/long-token-provision branch 3 times, most recently from 2fa5f27 to f434a52 Compare February 17, 2026 17:48
@v3nant v3nant force-pushed the feat/long-token-provision branch from f434a52 to ca5cdb6 Compare February 18, 2026 18:53
@v3nant v3nant force-pushed the feat/long-token-provision branch from ca5cdb6 to 1448d20 Compare February 18, 2026 19:12
@v3nant v3nant force-pushed the feat/long-token-provision branch from 1448d20 to 109514a Compare February 18, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments